
package com.tansun.easycare.rule.policytree.service;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSON;
import com.tansun.easycare.rule.policytree.model.PolicyTreeItem;
import com.tansun.easycare.rule.policytree.model.PolicyTreeKey;
import com.tansun.easycare.rule.policytree.model.PolicyTreeModel;

/**
 * 获取策略树html片段代码
 */
public class PolicyTreeHtml {
    private final String ROOT = "ROOT_V";
    private final int TOP = 0; // 顶层
    private int floorNum = 0; // 树的层数
    private List<PolicyTreeKey> keys; // 决策键 {floor,data}
    private PolicyTreeItem root; // 根节点
    private Map<String, List<PolicyTreeItem>> pMc; // 节点与子节点对应关系{parent,children}

    public PolicyTreeHtml(PolicyTreeModel entity) {
        convertData(entity);
    }

    /**
     * 数据转换
     */
    private void convertData(PolicyTreeModel entity) {
        keys = entity.getPolicyTreeKey();
        pMc = new HashMap();
        List<PolicyTreeItem> nodes = entity.getPolicyTreeItem();
        for (PolicyTreeItem node : nodes) {
            if (ROOT.equals(node.getId())) {
                root = node;
                floorNum = Integer.parseInt(root.getFloorNum());
            }
            String pid = node.getPid();
            List<PolicyTreeItem> children = pMc.get(pid);
            if (children == null) {
                children = new ArrayList<PolicyTreeItem>();
                pMc.put(pid, children);
            }
            children.add(node);
        }
    }

    /**
     * 获取策略树html片段代码
     *
     * @return 策略树html代码
     */
    public String getHtml() {
        if (root == null || pMc == null || keys == null) {
            return "";
        }
        StringBuffer html = new StringBuffer();
        createChildren(root, TOP, html);
        html.insert(0, getKeys());
        return html.toString();
    }

    /**
     * 递归创建子节点
     *
     * @param node         当前结点
     * @param currentFloor 当前层
     */
    private void createChildren(PolicyTreeItem node, int currentFloor, StringBuffer html) {
        int floor = node.getFloor();
        List<PolicyTreeItem> children = pMc.get(node.getId());
        int size = children == null ? 0 : children.size();
        if (size != 0) { // 非叶子节点
            if (currentFloor + 1 != children.get(0).getFloor()) {
                size = 1;
            }
            html.append(MessageFormat.format("<table {0}>\n", floor == 0 ? "floorNum='" + floorNum + "'" : ""));
            html.append("<tbody>\n");
            html.append(trNode(node));
            html.append(trLineDown(size));
            html.append(trLineBranch(size));
            html.append("<tr>");
            addNullFloor(currentFloor + 1, children.get(0).getFloor(), children, html);
            html.append("</tr>\n");
            html.append("</tbody>\n");
            html.append("</table>\n");
        } else {
            html.append(createLeafNode(node, currentFloor));
        }
    }

    /**
     * @param currentFloor 当前层
     * @param floor        节点所在层
     * @param c            孩子节点
     */
    private void addNullFloor(int currentFloor, int floor, List<PolicyTreeItem> children, StringBuffer html) {
        if (currentFloor == floor) {
            for (PolicyTreeItem c : children) {
                html.append("<td class='node-container' colspan=2>\n");
                createChildren(c, c.getFloor(), html);
                html.append("</td>\n");
            }
        } else if ("true".equals(children.get(0).getIsLeaf())) {
            html.append("<td class='node-container' colspan=2>\n");
            createChildren(children.get(0), currentFloor, html);
            html.append("</td>\n");
        } else {
            int drop = floor - currentFloor;
            int size = drop != 1 ? 1 : children.size();
            PolicyTreeItem node = new PolicyTreeItem();
            node.setFloor(currentFloor);
            html.append("<td class='node-container' colspan=2>\n");
            html.append("<table>\n");
            html.append("<tbody>\n");
            html.append(trNode(node));
            html.append(trLineDown(size));
            html.append(trLineBranch(size));
            html.append("<tr>");
            addNullFloor(currentFloor + 1, floor, children, html);
            html.append("</tr>\n");
            html.append("</tbody>\n");
            html.append("</table>\n");
            html.append("</td>\n");
        }
    }

    /**
     * 创建叶子节点
     *
     * @param node         叶子节点
     * @param currentFloor 递归到的层
     * @return html代码片段
     */
    private String createLeafNode(PolicyTreeItem node, int currentFloor) {
        StringBuffer tree = new StringBuffer();
        for (int i = currentFloor; i < floorNum; i++) {
            if (i < floorNum - 1) {
                tree.append("<div class='null-node' floor=" + i + "></div>");
            } else {
                tree.append("  <table>");
                tree.append("    <tbody>");
                node.setFloor(i);
                tree.append(trNode(node));
                tree.append("    </tbody>");
                tree.append("</table>");
            }
        }
        return tree.toString();
    }

    /**
     * 获取决策键代码段
     */
    private String getKeys() {
        StringBuffer sf = new StringBuffer();
        sf.append("<div class='keys'>\n");
        for (PolicyTreeKey key : keys) {
            int floor = key.getFloor();
            String keyStr = "<div class=\"key {0} \"><span class='key-value' floor={1} data={2}>{3}</span></div>\n";
            String json = JSON.toJSONString(key).replace(" ", "");
            json = StringEscapeUtils.escapeXml11(json);
            sf.append(MessageFormat.format(keyStr, floor == 0 ? " first" : "", floor, json, key.getName()));
        }
        sf.append("</div>\n");
        return sf.toString();
    }

    /**
     * 获取点的内容显示区域html代码段
     */
    private String trNode(PolicyTreeItem node) { //树节点内容存储行
        StringBuffer tr = new StringBuffer();
        tr.append("<tr class='node-cells' >");
        tr.append("    <td class='node-cell' colspan=2>");
        if (StringUtils.isBlank(node.getName())) {
            tr.append(MessageFormat.format("<div class=null-node floor={0}></div>", node.getFloor()));
        } else {
            String json = JSON.toJSONString(node).replace(" ", "");
            json = StringEscapeUtils.escapeXml11(json);
            String content = node.getName();
            String[] splited = node.getName().split("\\u0029");
            if ("true".equals(node.getIsLeaf()) && splited.length == 2) { //分离序号和叶子节点内容
                tr.append(MessageFormat.format("<div id={0} class=node floor={1} data={2}>{3}</div>", node.getId(), node.getFloor(), json, splited[1]));
                tr.append("<div class='leaf-index'>" + splited[0] + ")</div>");
            } else {
                tr.append(MessageFormat.format("<div id={0} class=node floor={1} data={2}>{3}</div>", node.getId(), node.getFloor(), json, content));
            }
        }
        tr.append("    </td>");
        tr.append("</tr>");
        return tr.toString();
    }

    /**
     * 获取分支线代码片段
     *
     * @param size 分支数
     */
    private String trLineDown(int size) { // | 线
        if (size == 0) {
            return "";
        }
        String tr = "<tr>";
        tr += "	<td colspan=" + (size * 2) + "><div class='line down'></div></td>";
        tr += "</tr>";
        return tr;
    }

    /**
     * 获取分支线代码片段
     *
     * @param size 分支数
     */
    private String trLineBranch(int size) { // 分支线
        if (size == 0) {
            return "";
        }
        StringBuffer tr2 = new StringBuffer();
        tr2.append("<tr>");
        for (int i = 0; i < size; i++) {
            tr2.append("<td class='line'>");
            tr2.append("  <div class='left-box " + (i == 0 ? "" : "top") + "'>&nbsp;</div>");
            tr2.append("  <div class='right-box " + (i == size - 1 ? "" : "top") + "'>&nbsp;</div>");
            tr2.append("</td>");
            if (size == 1) {
                tr2.append("<td class='line inner3'></td>");
            } else {
                tr2.append("<td class='line " + (i == size - 1 ? "" : "top") + "'></td>");
            }
        }
        return tr2.toString();
    }
}
